#include "JpegDecoder.h"

JpegDecoder::JpegDecoder(void * inputData, int length, void * outputData, int depth)
: inputData(inputData),
 length(length),
 outputData(outputData),
 depth(depth),
 jpegDecoderXX(NULL){

}

void JpegDecoder::initData(void * inputData, int length, void * outputData, int depth){
	this->inputData = inputData;
	this->length = length;
	this->outputData = outputData;
	this->depth = depth;
}


JpegDecoder::JpegDecoder(void)
:jpegDecoderXX(NULL)
{

}
JpegDecoder::~JpegDecoder(void){
	delete jpegDecoderXX;
}

BOOLEAN JpegDecoder::decompress(){
	if(length == 0 || inputData == NULL || outputData == false)
		return false;
	int depth = scanJpegDataForBitDepth((const unsigned char *)inputData, length);
	delete jpegDecoderXX;
	jpegDecoderXX = NULL;
	if(this->depth != depth && jpegDecoderXX != NULL)
	{
		delete jpegDecoderXX;
		jpegDecoderXX = NULL;
	}
	if(jpegDecoderXX == NULL){		
		if (depth > 12)
			jpegDecoderXX = new JpegDecoder16(inputData, length, outputData);
		else if (depth > 8)
			jpegDecoderXX = new JpegDecoder12(inputData, length, outputData);
		else
			jpegDecoderXX = new JpegDecoder08(inputData, length, outputData);
		
	}else{
		jpegDecoderXX->initData(inputData, length, outputData);
	}
	jpegDecoderXX->decompress();
	inputData = NULL;
	outputData = NULL;
	return true;
}

unsigned short JpegDecoder::readshort(const unsigned char *data)
{
	return (((unsigned short)(*data) << 8) | ((unsigned short)(*(data + 1))));
}


unsigned char JpegDecoder::scanJpegDataForBitDepth(
	const unsigned char *data,
	const unsigned int fragmentLength){
		unsigned int offset = 0;
		while(offset + 4 < fragmentLength)
		{
			switch(readshort(data + offset))
			{
			case 0xffc0: // SOF_0: JPEG baseline
				return data[offset + 4];
				/* break; */
			case 0xffc1: // SOF_1: JPEG extended sequential DCT
				return data[offset + 4];
				/* break; */
			case 0xffc2: // SOF_2: JPEG progressive DCT
				return data[offset + 4];
				/* break; */
			case 0xffc3 : // SOF_3: JPEG lossless sequential
				return data[offset + 4];
				/* break; */
			case 0xffc5: // SOF_5: differential (hierarchical) extended sequential, Huffman
				return data[offset + 4];
				/* break; */
			case 0xffc6: // SOF_6: differential (hierarchical) progressive, Huffman
				return data[offset + 4];
				/* break; */
			case 0xffc7: // SOF_7: differential (hierarchical) lossless, Huffman
				return data[offset + 4];
				/* break; */
			case 0xffc8: // Reserved for JPEG extentions
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xffc9: // SOF_9: extended sequential, arithmetic
				return data[offset + 4];
				/* break; */
			case 0xffca: // SOF_10: progressive, arithmetic
				return data[offset+4];
				/* break; */
			case 0xffcb: // SOF_11: lossless, arithmetic
				return data[offset+4];
				/* break; */
			case 0xffcd: // SOF_13: differential (hierarchical) extended sequential, arithmetic
				return data[offset+4];
				/* break; */
			case 0xffce: // SOF_14: differential (hierarchical) progressive, arithmetic
				return data[offset + 4];
				/* break; */
			case 0xffcf: // SOF_15: differential (hierarchical) lossless, arithmetic
				return data[offset + 4];
				/* break; */
			case 0xffc4: // DHT
				offset += readshort(data+offset + 2) + 2;
				break;
			case 0xffcc: // DAC
				offset += readshort(data+offset + 2) + 2;
				break;
			case 0xffd0: // RST m
			case 0xffd1:
			case 0xffd2:
			case 0xffd3:
			case 0xffd4:
			case 0xffd5:
			case 0xffd6:
			case 0xffd7:
				offset +=2;
				break;
			case 0xffd8: // SOI
				offset +=2;
				break;
			case 0xffd9: // EOI
				offset +=2;
				break;
			case 0xffda: // SOS
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xffdb: // DQT
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xffdc: // DNL
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xffdd: // DRI
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xffde: // DHP
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xffdf: // EXP
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xffe0: // APPn
			case 0xffe1:
			case 0xffe2:
			case 0xffe3:
			case 0xffe4:
			case 0xffe5:
			case 0xffe6:
			case 0xffe7:
			case 0xffe8:
			case 0xffe9:
			case 0xffea:
			case 0xffeb:
			case 0xffec:
			case 0xffed:
			case 0xffee:
			case 0xffef:
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xfff0: // JPGn
			case 0xfff1:
			case 0xfff2:
			case 0xfff3:
			case 0xfff4:
			case 0xfff5:
			case 0xfff6:
			case 0xfff7:
			case 0xfff8:
			case 0xfff9:
			case 0xfffa:
			case 0xfffb:
			case 0xfffc:
			case 0xfffd:
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xfffe: // COM
				offset += readshort(data + offset + 2) + 2;
				break;
			case 0xff01: // TEM
				break;
			default:
				if ((data[offset]==0xff) && (data[offset + 1] > 2) && (data[offset + 1] <= 0xbf)) // RES reserved markers
				{
					offset += 2;
				}
				else return 0; // syntax error, stop parsing
				break;
			}
		} // while
		return 0; // no SOF marker found
}


BOOLEAN JpegDecoder::createPlanarConfigurationWord(
	unsigned short *imageFrame,
	unsigned short columns,
	unsigned short rows)
{
	if (imageFrame == NULL) return false;

	unsigned long numPixels = columns * rows;
	if (numPixels == 0) return false;

	unsigned short *buf = new unsigned short[3*numPixels + 3];
	if (buf)
	{
		memcpy(buf, imageFrame, (size_t)(3*numPixels*sizeof(unsigned short)));
		register unsigned short *s = buf;                        // source
		register unsigned short *r = imageFrame;                 // red plane
		register unsigned short *g = imageFrame + numPixels;     // green plane
		register unsigned short *b = imageFrame + (2*numPixels); // blue plane
		for (register unsigned long i=numPixels; i; i--)
		{
			*r++ = *s++;
			*g++ = *s++;
			*b++ = *s++;
		}
		delete[] buf;
	} else return false;
	return true;
}